/*
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.imagepipeline.bitmaps;

import com.facebook.common.memory.PooledByteBuffer;
import com.facebook.common.memory.PooledByteBufferFactory;
import com.facebook.common.memory.PooledByteBufferOutputStream;
import com.facebook.common.references.CloseableReference;
import java.io.IOException;

/**
 * Producer that emits a dummy, fixed-size JPEG bytearray.
 *
 * <p>The purpose of this jpeg is to serve as the source for a purgeable bitmap which will
 * in turn have its contents overwritten, enabling us to copy into a purgeable bitmap.
 */
public class EmptyJpegGenerator {

  // The following JPEG was generated by compressing a 1x1 bitmap on an Android device
  // and logcatting the output.

  private static final byte[] EMPTY_JPEG_PREFIX = new byte[]{
      (byte) 0xff, (byte) 0xd8, // Start of Image marker

      // Quantization table
      (byte) 0xff, (byte) 0xdb, // DQT marker
      (byte) 0x00, (byte) 0x43, // Length
      (byte) 0x00, // Precision: 0 Id: 0
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
      (byte) 0xff,

      // Frame data
      (byte) 0xff, (byte) 0xc0, // SOF marker
      (byte) 0x00, (byte) 0x11, // Frame length
      (byte) 0x08, // Sample precision
  };

  // Width and height (16 bits each) are inserted between these two

  private static final byte[] EMPTY_JPEG_SUFFIX = new byte[]{
      (byte) 0x03, // Number of components
      (byte) 0x01, // Component 1
      (byte) 0x22, // Sampling factor
      (byte) 0x00, // Quantization selector
      (byte) 0x02, // Component 2
      (byte) 0x11, // Sampling factor
      (byte) 0x00, // Quantization selector
      (byte) 0x03, // Component 3
      (byte) 0x11, // Sampling factor
      (byte) 0x00, // Quantization selector

      // Huffman table
      (byte) 0xff, (byte) 0xc4, // DHT marker
      (byte) 0x00, (byte) 0x1f, // Length
      (byte) 0x00, // Table class: DC id: 0
      // Huffman code lengths
      (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
      (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
      (byte) 0x00, (byte) 0x00,
      // Huffman code values
      (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
      (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0xff, (byte) 0xc4,
      (byte) 0x00, (byte) 0xb5, (byte) 0x10, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x03,
      (byte) 0x03, (byte) 0x02, (byte) 0x04, (byte) 0x03, (byte) 0x05, (byte) 0x05, (byte) 0x04,
      (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x7d, (byte) 0x01, (byte) 0x02,
      (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x11, (byte) 0x05, (byte) 0x12, (byte) 0x21,
      (byte) 0x31, (byte) 0x41, (byte) 0x06, (byte) 0x13, (byte) 0x51, (byte) 0x61, (byte) 0x07,
      (byte) 0x22, (byte) 0x71, (byte) 0x14, (byte) 0x32, (byte) 0x81, (byte) 0x91, (byte) 0xa1,
      (byte) 0x08, (byte) 0x23, (byte) 0x42, (byte) 0xb1, (byte) 0xc1, (byte) 0x15, (byte) 0x52,
      (byte) 0xd1, (byte) 0xf0, (byte) 0x24, (byte) 0x33, (byte) 0x62, (byte) 0x72, (byte) 0x82,
      (byte) 0x09, (byte) 0x0a, (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a,
      (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2a, (byte) 0x34,
      (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3a, (byte) 0x43,
      (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4a,
      (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56,
      (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5a, (byte) 0x63, (byte) 0x64, (byte) 0x65,
      (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x73, (byte) 0x74,
      (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7a, (byte) 0x83,
      (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8a,
      (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98,
      (byte) 0x99, (byte) 0x9a, (byte) 0xa2, (byte) 0xa3, (byte) 0xa4, (byte) 0xa5, (byte) 0xa6,
      (byte) 0xa7, (byte) 0xa8, (byte) 0xa9, (byte) 0xaa, (byte) 0xb2, (byte) 0xb3, (byte) 0xb4,
      (byte) 0xb5, (byte) 0xb6, (byte) 0xb7, (byte) 0xb8, (byte) 0xb9, (byte) 0xba, (byte) 0xc2,
      (byte) 0xc3, (byte) 0xc4, (byte) 0xc5, (byte) 0xc6, (byte) 0xc7, (byte) 0xc8, (byte) 0xc9,
      (byte) 0xca, (byte) 0xd2, (byte) 0xd3, (byte) 0xd4, (byte) 0xd5, (byte) 0xd6, (byte) 0xd7,
      (byte) 0xd8, (byte) 0xd9, (byte) 0xda, (byte) 0xe1, (byte) 0xe2, (byte) 0xe3, (byte) 0xe4,
      (byte) 0xe5, (byte) 0xe6, (byte) 0xe7, (byte) 0xe8, (byte) 0xe9, (byte) 0xea, (byte) 0xf1,
      (byte) 0xf2, (byte) 0xf3, (byte) 0xf4, (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8,
      (byte) 0xf9, (byte) 0xfa, (byte) 0xff, (byte) 0xc4, (byte) 0x00, (byte) 0x1f, (byte) 0x01,
      (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
      (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
      (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b,

      (byte) 0xff, (byte) 0xc4, // DHT marker
      (byte) 0x00, (byte) 0xb5, // Length
      (byte) 0x11, // Table class: AC id: 1
      // Huffman code lengths
      (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x04, (byte) 0x03,
      (byte) 0x04, (byte) 0x07, (byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x01,
      (byte) 0x02, (byte) 0x77,
      // Huffman code values
      (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x11, (byte) 0x04, (byte) 0x05,
      (byte) 0x21, (byte) 0x31, (byte) 0x06, (byte) 0x12, (byte) 0x41, (byte) 0x51, (byte) 0x07,
      (byte) 0x61, (byte) 0x71, (byte) 0x13, (byte) 0x22, (byte) 0x32, (byte) 0x81, (byte) 0x08,
      (byte) 0x14, (byte) 0x42, (byte) 0x91, (byte) 0xa1, (byte) 0xb1, (byte) 0xc1, (byte) 0x09,
      (byte) 0x23, (byte) 0x33, (byte) 0x52, (byte) 0xf0, (byte) 0x15, (byte) 0x62, (byte) 0x72,
      (byte) 0xd1, (byte) 0x0a, (byte) 0x16, (byte) 0x24, (byte) 0x34, (byte) 0xe1, (byte) 0x25,
      (byte) 0xf1, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a, (byte) 0x26, (byte) 0x27,
      (byte) 0x28, (byte) 0x29, (byte) 0x2a, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38,
      (byte) 0x39, (byte) 0x3a, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
      (byte) 0x48, (byte) 0x49, (byte) 0x4a, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56,
      (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5a, (byte) 0x63, (byte) 0x64, (byte) 0x65,
      (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x73, (byte) 0x74,
      (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7a, (byte) 0x82,
      (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89,
      (byte) 0x8a, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97,
      (byte) 0x98, (byte) 0x99, (byte) 0x9a, (byte) 0xa2, (byte) 0xa3, (byte) 0xa4, (byte) 0xa5,
      (byte) 0xa6, (byte) 0xa7, (byte) 0xa8, (byte) 0xa9, (byte) 0xaa, (byte) 0xb2, (byte) 0xb3,
      (byte) 0xb4, (byte) 0xb5, (byte) 0xb6, (byte) 0xb7, (byte) 0xb8, (byte) 0xb9, (byte) 0xba,
      (byte) 0xc2, (byte) 0xc3, (byte) 0xc4, (byte) 0xc5, (byte) 0xc6, (byte) 0xc7, (byte) 0xc8,
      (byte) 0xc9, (byte) 0xca, (byte) 0xd2, (byte) 0xd3, (byte) 0xd4, (byte) 0xd5, (byte) 0xd6,
      (byte) 0xd7, (byte) 0xd8, (byte) 0xd9, (byte) 0xda, (byte) 0xe2, (byte) 0xe3, (byte) 0xe4,
      (byte) 0xe5, (byte) 0xe6, (byte) 0xe7, (byte) 0xe8, (byte) 0xe9, (byte) 0xea, (byte) 0xf2,
      (byte) 0xf3, (byte) 0xf4, (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9,
      (byte) 0xfa,

      // Scan data
      (byte) 0xff, (byte) 0xda, // Start of Scan marker
      (byte) 0x00, (byte) 0x0c, // Scan header length
      (byte) 0x03, // Number of components
      (byte) 0x01, // Component 1
      (byte) 0x00, // Coding table selector
      (byte) 0x02, // Start spectral selectin
      (byte) 0x11, // End spectral selection
      (byte) 0x03, // Approximation bits

      // Huffman-encoded data
      (byte) 0x11, (byte) 0x00, (byte) 0x3f, (byte) 0x00, (byte) 0x8e, (byte) 0x8a, (byte) 0x28,
      (byte) 0xa0, (byte) 0x0f,

      // End of Image
      (byte) 0xff, (byte) 0xd9, // EOI marker
  };

  private final PooledByteBufferFactory mPooledByteBufferFactory;

  public EmptyJpegGenerator(PooledByteBufferFactory pooledByteBufferFactory) {
    mPooledByteBufferFactory = pooledByteBufferFactory;
  }

  public CloseableReference<PooledByteBuffer> generate(short width, short height) {
    PooledByteBufferOutputStream os = null;
    try {
      os = mPooledByteBufferFactory.newOutputStream(
          EMPTY_JPEG_PREFIX.length + EMPTY_JPEG_SUFFIX.length + 4);
      os.write(EMPTY_JPEG_PREFIX);
      os.write((byte) (height >> 8));
      os.write((byte) (height & 0x00ff));
      os.write((byte) (width >> 8));
      os.write((byte) (width & 0x00ff));
      os.write(EMPTY_JPEG_SUFFIX);
      return CloseableReference.of(os.toByteBuffer());
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if (os != null) {
        os.close();
      }
    }
  }
}
